package com.wcs.exam.controller.admin.biz;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.PageDTO;
import com.wcs.exam.common.base.BaseBiz;
import com.wcs.exam.common.base.Result;
import com.wcs.exam.common.base.page.Page;
import com.wcs.exam.common.base.page.PageUtil;
import com.wcs.exam.common.base.page.SqlUtil;
import com.wcs.exam.common.enums.*;
import com.wcs.exam.common.util.BeanUtil;
import com.wcs.exam.controller.admin.req.*;
import com.wcs.exam.controller.admin.resp.*;
import com.wcs.exam.controller.biz.ExamCommonBiz;
import com.wcs.exam.controller.biz.QuestionCommonBiz;
import com.wcs.exam.controller.biz.resp.QuestionDetailResp;
import com.wcs.exam.dao.*;
import com.wcs.exam.dao.impl.mapper.entity.*;
import com.wcs.exam.dao.po.ExamUserRankPO;
import com.wcs.exam.dao.po.ExamUserRecordExamCountPO;
import com.wcs.exam.dao.po.ExamUserRecordUserCountPO;
import jakarta.validation.constraints.NotNull;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * Admin-考试-监控数据
 *
 * @author wcs
 * @since 2024-07-10
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class AdminExamDataBiz extends BaseBiz {

    @NotNull
    private final ExamCommonBiz examCommonBiz;
    @NotNull
    private final QuestionCommonBiz questionCommonBiz;

    @NotNull
    private final ExamDao examDao;
    @NotNull
    private final ExamUserDao examUserDao;
    @NotNull
    private final ExamUserRecordDao examUserRecordDao;
    @NotNull
    private final ExamUserAnswerDao examUserAnswerDao;
    @NotNull
    private final PaperDao paperDao;
    @NotNull
    private final PaperTitleDao paperTitleDao;
    @NotNull
    private final PaperQuestionDao paperQuestionDao;
    @NotNull
    private final QuestionDao questionDao;
    @NotNull
    private final UsersDao usersDao;

    /**
     * 考试-监控数据-统计分析
     *
     * @param req 请求参数
     * @return 统计分析
     */
    public Result<AdminExamDataStatisticsResp> statistics(AdminExamDataStatisticsReq req) {
        AdminExamDataStatisticsResp resp = new AdminExamDataStatisticsResp()
                .setTotalScore(BigDecimal.ZERO)
                .setPassScore(BigDecimal.ZERO)
                .setHighestScore(BigDecimal.ZERO)
                .setLowestScore(BigDecimal.ZERO)
                .setAverageScore(BigDecimal.ZERO)
                .setPassRate(BigDecimal.ZERO)
                .setPassUserCount(0L)
                .setExamUserCount(0L)
                .setUserCount(0L);

        // 查询用户
        Set<Long> userIds = new HashSet<>();
        Boolean userExist = Boolean.TRUE;
        if (StrUtil.isNotBlank(req.getNickname()) || StrUtil.isNotBlank(req.getMobile())) {
            List<Users> userList = usersDao.lambdaQuery()
                    .like(StrUtil.isNotBlank(req.getNickname()), Users::getNickname, req.getNickname())
                    .like(StrUtil.isNotBlank(req.getMobile()), Users::getMobile, req.getMobile())
                    .list();
            if (CollectionUtil.isNotEmpty(userList)) {
                userList.forEach(item -> userIds.add(item.getId()));
            } else {
                userExist = Boolean.FALSE;
            }
        }

        if (ExamTypeEnum.MOCK.getCode().equals(req.getExamType()) || ExamTypeEnum.EXAMINATION.getCode().equals(req.getExamType())) {
            // 模拟考试、正式考试
            return handleExamStatistics(req, resp, userExist, userIds);
        } else {
            // 其他类型暂不支持
            return Result.success(resp);
        }
    }

    /**
     * 模拟考试/正式考试考试统计
     *
     * @param req       统计参数
     * @param resp      统计结果
     * @param userExist 用户是否存在
     * @param userIds   用户ID
     * @return 统计结果
     */
    private Result<AdminExamDataStatisticsResp> handleExamStatistics(AdminExamDataStatisticsReq req, AdminExamDataStatisticsResp resp, Boolean userExist, Set<Long> userIds) {
        Exam exam = examDao.getById(req.getRelateId());
        if (ObjectUtil.isNull(exam)) {
            return Result.error(toI18n("resource.exam.not.exist"));
        }
        Paper paper = paperDao.getById(exam.getPaperId());
        if (ObjectUtil.isNull(paper)) {
            return Result.error(toI18n("resource.paper.not.exist"));
        }

        // 计算合格分
        BigDecimal passScore;
        if (exam.getPassPercentage() > 0) {
            passScore = paper.getTotalScore()
                    .multiply(BigDecimal.valueOf(exam.getPassPercentage()))
                    .divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_UP);
        } else {
            passScore = BigDecimal.ZERO;
        }
        resp.setTotalScore(paper.getTotalScore());
        resp.setPassScore(passScore);

        if (Boolean.FALSE.equals(userExist)) {
            return Result.success(resp);
        }

        List<ExamUser> examUserList = examUserDao.lambdaQuery()
                .eq(ExamUser::getExamType, req.getExamType())
                .eq(ExamUser::getExamId, req.getRelateId())
                .in(CollectionUtil.isNotEmpty(userIds), ExamUser::getUserId, userIds)
                .list();
        if (CollectionUtil.isEmpty(examUserList)) {
            return Result.success(resp);
        }
        // 统计总人数
        resp.setUserCount((long) examUserList.size());

        // 统计最高分
        BigDecimal highestScore = examUserList.stream()
                .filter(item -> !UserExamStatusEnum.WAIT_EXAM.getCode().equals(item.getUserExamStatus()))
                .map(ExamUser::getUserScore)
                .max(BigDecimal::compareTo)
                .orElse(BigDecimal.ZERO);
        resp.setHighestScore(highestScore);

        // 统计最低分
        BigDecimal lowestScore = examUserList.stream()
                .filter(item -> !UserExamStatusEnum.WAIT_EXAM.getCode().equals(item.getUserExamStatus()))
                .map(ExamUser::getUserScore)
                .min(BigDecimal::compareTo)
                .orElse(BigDecimal.ZERO);
        resp.setLowestScore(lowestScore);


        // 考试人数
        long examUserCount = examUserList.stream()
                .filter(item -> !UserExamStatusEnum.WAIT_EXAM.getCode().equals(item.getUserExamStatus()))
                .count();
        resp.setExamUserCount(examUserCount);

        // 平均分
        if (examUserCount > 0) {
            BigDecimal totalUserScore = examUserList.stream()
                    .filter(item -> !UserExamStatusEnum.WAIT_EXAM.getCode().equals(item.getUserExamStatus()))
                    .map(ExamUser::getUserScore)
                    .reduce(BigDecimal.ZERO, BigDecimal::add);
            BigDecimal averageScore = totalUserScore.divide(BigDecimal.valueOf(examUserCount), 2, RoundingMode.HALF_UP);
            resp.setAverageScore(averageScore);


            // 及格人数
            long passUserCount = examUserList.stream()
                    .filter(item -> !UserExamStatusEnum.WAIT_EXAM.getCode().equals(item.getUserExamStatus()))
                    .filter(item -> item.getUserScore().compareTo(passScore) >= 0)
                    .count();
            resp.setPassUserCount(passUserCount);

            // 及格率
            resp.setPassRate(BigDecimal.valueOf(passUserCount).divide(BigDecimal.valueOf(examUserCount), 2, RoundingMode.HALF_UP));
        }
        return Result.success(resp);
    }


    /**
     * 考试-监控数据-用户-分页
     *
     * @param req 分页查询参数
     * @return 监控数据
     */
    public Result<Page<AdminExamDataUserPageResp>> userPage(AdminExamDataUserPageReq req) {
        if (ExamTypeEnum.MOCK.getCode().equals(req.getExamType()) || ExamTypeEnum.EXAMINATION.getCode().equals(req.getExamType())) {
            return handleExamUserPage(req);
        } else {
            return Result.error(toI18n("resource.exam.data.exam.type.not.supported.yet"));
        }
    }

    /**
     * 模拟/正式考试-监控数据-用户-分页
     *
     * @param req 查询参数
     * @return 监控数据
     */
    private Result<Page<AdminExamDataUserPageResp>> handleExamUserPage(AdminExamDataUserPageReq req) {
        // 查询用户
        Map<Long, Users> userMap = new HashMap<>();
        if (StrUtil.isNotBlank(req.getNickname()) || StrUtil.isNotBlank(req.getMobile())) {
            List<Users> userList = usersDao.lambdaQuery()
                    .like(StrUtil.isNotBlank(req.getNickname()), Users::getNickname, req.getNickname())
                    .like(StrUtil.isNotBlank(req.getMobile()), Users::getMobile, req.getMobile())
                    .list();
            if (CollectionUtil.isEmpty(userList)) {
                return Result.success(new Page<>());
            }
            userList.forEach(item -> userMap.put(item.getId(), item));
        }

        LocalDateTime endRegistrationTime = ObjectUtil.isNotNull(req.getEndRegistrationTime()) ? req.getEndRegistrationTime().plusMinutes(1) : null;
        LocalDateTime endExamStartTime = ObjectUtil.isNotNull(req.getEndExamStartTime()) ? req.getEndExamStartTime().plusMinutes(1) : null;
        LocalDateTime endExamSubmitTime = ObjectUtil.isNotNull(req.getEndExamSubmitTime()) ? req.getEndExamSubmitTime().plusMinutes(1) : null;

        // 查询考试记录
        LambdaQueryWrapper<ExamUser> wrapper = new LambdaQueryWrapper<ExamUser>()
                .eq(ExamUser::getExamId, req.getRelateId())
                .in(CollectionUtil.isNotEmpty(userMap), ExamUser::getUserId, userMap.keySet())
                .ge(ObjectUtil.isNotNull(req.getBeginRegistrationTime()), ExamUser::getRegistrationTime, req.getBeginRegistrationTime())
                .lt(ObjectUtil.isNotNull(endRegistrationTime), ExamUser::getRegistrationTime, endRegistrationTime)
                .ge(ObjectUtil.isNotNull(req.getBeginExamStartTime()), ExamUser::getExamStartTime, req.getBeginExamStartTime())
                .lt(ObjectUtil.isNotNull(endExamStartTime), ExamUser::getExamStartTime, endExamStartTime)
                .ge(ObjectUtil.isNotNull(req.getBeginExamSubmitTime()), ExamUser::getExamSubmitTime, req.getBeginExamSubmitTime())
                .lt(ObjectUtil.isNotNull(endExamSubmitTime), ExamUser::getExamSubmitTime, endExamSubmitTime)
                .orderByDesc(ExamUser::getExamStartTime, ExamUser::getId);
        IPage<ExamUser> page = examUserDao.page(PageDTO.of(req.getPageCurrent(), req.getPageSize()), wrapper);
        Page<AdminExamDataUserPageResp> respPage = PageUtil.transform(page, AdminExamDataUserPageResp.class);
        if (ObjectUtil.isNull(respPage) || CollectionUtil.isEmpty(respPage.getList())) {
            return Result.success(respPage);
        }
        Set<Long> userIds = respPage.getList().stream().map(AdminExamDataUserPageResp::getUserId).collect(Collectors.toSet());

        // 获取用户信息
        if (CollectionUtil.isEmpty(userMap)) {
            List<Users> usersList = usersDao.listByIds(userIds);
            if (CollectionUtil.isNotEmpty(usersList)) {
                usersList.forEach(item -> userMap.put(item.getId(), item));
            }
        }

        // 统计用户考试次数
        List<ExamUserRecordUserCountPO> examCountList = examUserRecordDao.userCountByExamTypeAndRelateId(req.getExamType(), req.getRelateId(), userIds);
        Map<Long, Long> examCountMap = new HashMap<>();
        if (CollectionUtil.isNotEmpty(examCountList)) {
            examCountList.forEach(item -> examCountMap.put(item.getUserId(), item.getCount()));
        }

        // 获取阅卷方式
        Set<Long> recordIds = respPage.getList().stream()
                .map(AdminExamDataUserPageResp::getExamRecordId)
                .filter(ObjectUtil::isNotNull)
                .collect(Collectors.toSet());
        List<ExamUserRecord> userRecordList = examUserRecordDao.listByIds(recordIds);
        Map<Long, ExamUserRecord> userRecordMap = new HashMap<>();
        if (CollectionUtil.isNotEmpty(userRecordList)) {
            userRecordList.forEach(item -> userRecordMap.put(item.getId(), item));
        }

        for (AdminExamDataUserPageResp resp : respPage.getList()) {
            Users user = userMap.get(resp.getUserId());
            if (ObjectUtil.isNotNull(user)) {
                resp.setUserHead(user.getUserHead());
                resp.setNickname(user.getNickname());
                resp.setMobile(user.getMobile());
            }

            ExamUserRecord examUserRecord = userRecordMap.get(resp.getExamRecordId());
            if (ObjectUtil.isNotNull(examUserRecord)) {
                resp.setEvaluationMethod(examUserRecord.getEvaluationMethod());
            }

            resp.setExamCount(examCountMap.getOrDefault(resp.getUserId(), 0L));
        }
        return Result.success(respPage);
    }

    /**
     * 考试-监控数据-用户-答案-编辑
     *
     * @param req 编辑请求参数
     * @return 编辑结果
     */
    public Result<String> userAnswerEdit(AdminExamDataUserAnswerEditReq req) {
        ExamUserRecord examUserRecord = examUserRecordDao.getById(req.getRecordId());
        if (ObjectUtil.isNull(examUserRecord)) {
            return Result.error(toI18n("resource.exam.user.record.not.exist"));
        }
        if (ExamStatusEnum.NOT_OVER.getCode().equals(examUserRecord.getExamStatus())) {
            return Result.error(toI18n("resource.exam.user.record.in.the.exam"));
        }

        // 获取试卷
        Paper paper = paperDao.getById(examUserRecord.getPaperId());
        if (ObjectUtil.isNull(paper)) {
            return Result.error(toI18n("resource.paper.not.exist"));
        }

        // 获取用户答案
        ExamUserAnswer userAnswer = examUserAnswerDao.lambdaQuery()
                .eq(ExamUserAnswer::getRecordId, req.getRecordId())
                .eq(ExamUserAnswer::getTitleId, req.getTitleId())
                .eq(ExamUserAnswer::getQuestionId, req.getQuestionId())
                .last(SqlUtil.limitOne())
                .one();
        if (ObjectUtil.isNotNull(userAnswer)) {
            examUserAnswerDao.lambdaUpdate()
                    .eq(ExamUserAnswer::getId, userAnswer.getId())
                    .set(ExamUserAnswer::getUserAnswer, StrUtil.isNotBlank(req.getUserAnswer()) ? req.getUserAnswer() : "")
                    .update();
        } else {
            // 获取试题
            PaperQuestion paperQuestion = paperQuestionDao.lambdaQuery()
                    .eq(PaperQuestion::getPaperId, paper.getId())
                    .eq(PaperQuestion::getTitleId, req.getTitleId())
                    .eq(PaperQuestion::getQuestionId, req.getQuestionId())
                    .last(SqlUtil.limitOne())
                    .one();
            if (ObjectUtil.isNull(paperQuestion)) {
                return Result.error(toI18n("resource.question.not.exist"));
            }

            ExamUserAnswer savedUserAnswer = new ExamUserAnswer()
                    .setUserId(examUserRecord.getUserId())
                    .setRecordId(req.getRecordId())
                    .setExamType(examUserRecord.getExamType())
                    .setRelateId(examUserRecord.getRelateId())
                    .setPaperId(examUserRecord.getPaperId())
                    .setTitleId(req.getTitleId())
                    .setQuestionId(paperQuestion.getQuestionId())
                    .setQuestionType(paperQuestion.getQuestionType())
                    .setQuestionScore(paperQuestion.getQuestionScore())
                    .setUserAnswer(StrUtil.isNotBlank(req.getUserAnswer()) ? req.getUserAnswer() : "")
                    .setUserScore(BigDecimal.ZERO)
                    .setEvaluationComplete(Boolean.FALSE);
            examUserAnswerDao.save(savedUserAnswer);
        }
        return Result.success(toI18nSuccess());
    }

    /**
     * 考试-监控数据-记录-分页
     *
     * @param req 分页请求参数
     * @return 考试记录
     */
    public Result<Page<AdminExamDataRecordPageResp>> recordPage(AdminExamDataRecordPageReq req) {
        // 查询用户
        Map<Long, Users> userMap = new HashMap<>();
        if (StrUtil.isNotBlank(req.getNickname()) || StrUtil.isNotBlank(req.getMobile())) {
            List<Users> userList = usersDao.lambdaQuery()
                    .like(StrUtil.isNotBlank(req.getMobile()), Users::getMobile, req.getMobile())
                    .like(StrUtil.isNotBlank(req.getNickname()), Users::getNickname, req.getNickname())
                    .list();
            if (CollectionUtil.isEmpty(userList)) {
                return Result.success(new Page<>());
            }
            userList.forEach(item -> userMap.put(item.getId(), item));
        }

        LocalDateTime endExamStartTime = ObjectUtil.isNotNull(req.getEndExamStartTime()) ? req.getEndExamStartTime().plusMinutes(1) : null;
        LocalDateTime endSubmitTime = ObjectUtil.isNotNull(req.getEndSubmitTime()) ? req.getEndSubmitTime().plusMinutes(1) : null;


        LambdaQueryWrapper<ExamUserRecord> wrapper = new LambdaQueryWrapper<ExamUserRecord>()
                .eq(ExamUserRecord::getExamType, req.getExamType())
                .eq(ExamUserRecord::getRelateId, req.getRelateId())
                .eq(ObjectUtil.isNotNull(req.getUserId()), ExamUserRecord::getUserId, req.getUserId())
                .in(CollectionUtil.isNotEmpty(userMap), ExamUserRecord::getUserId, userMap.keySet())
                .ge(ObjectUtil.isNotNull(req.getBeginExamStartTime()), ExamUserRecord::getGmtCreate, req.getBeginExamStartTime())
                .lt(ObjectUtil.isNotNull(endExamStartTime), ExamUserRecord::getGmtCreate, endExamStartTime)
                .ge(ObjectUtil.isNotNull(req.getBeginSubmitTime()), ExamUserRecord::getSubmitTime, req.getBeginSubmitTime())
                .lt(ObjectUtil.isNotNull(endSubmitTime), ExamUserRecord::getSubmitTime, req.getEndSubmitTime())
                .eq(ObjectUtil.isNotNull(req.getExamStatus()), ExamUserRecord::getExamStatus, req.getExamStatus())
                .eq(ObjectUtil.isNotNull(req.getPassStatus()), ExamUserRecord::getPassStatus, req.getPassStatus())
                .orderByDesc(ExamUserRecord::getId);
        IPage<ExamUserRecord> page = examUserRecordDao.page(PageDTO.of(req.getPageCurrent(), req.getPageSize()), wrapper);
        Page<AdminExamDataRecordPageResp> respPage = PageUtil.transform(page, AdminExamDataRecordPageResp.class);
        if (ObjectUtil.isNull(respPage) || CollectionUtil.isEmpty(respPage.getList())) {
            return Result.success(respPage);
        }

        // 获取用户信息
        if (CollectionUtil.isEmpty(userMap)) {
            Set<Long> userIds = respPage.getList().stream().map(AdminExamDataRecordPageResp::getUserId).collect(Collectors.toSet());
            List<Users> usersList = usersDao.listByIds(userIds);
            if (CollectionUtil.isNotEmpty(usersList)) {
                usersList.forEach(item -> userMap.put(item.getId(), item));
            }
        }

        for (AdminExamDataRecordPageResp resp : respPage.getList()) {
            Users user = userMap.get(resp.getUserId());
            if (ObjectUtil.isNotNull(user)) {
                resp.setUserHead(user.getUserHead());
                resp.setNickname(user.getNickname());
                resp.setMobile(user.getMobile());
            }

            if (ExamStatusEnum.NOT_OVER.getCode().equals(resp.getExamStatus())) {
                resp.setUserEvaluationStatus(UserEvaluationStatusEnum.UN_KNOW.getCode());
                resp.setUserExamStatus(UserExamStatusEnum.DURING_EXAM.getCode());
            } else if (ExamStatusEnum.WAIT_CORRECTED.getCode().equals(resp.getExamStatus())) {
                resp.setUserEvaluationStatus(UserEvaluationStatusEnum.WAIT_MARKED.getCode());
                resp.setUserExamStatus(UserExamStatusEnum.EXAM_COMPLETE.getCode());
            } else if (ExamStatusEnum.CORRECTING.getCode().equals(resp.getExamStatus())) {
                resp.setUserEvaluationStatus(UserEvaluationStatusEnum.REVIEWING_PAPER.getCode());
                resp.setUserExamStatus(UserExamStatusEnum.EXAM_COMPLETE.getCode());
            } else if (ExamStatusEnum.COMPLETE.getCode().equals(resp.getExamStatus())) {
                resp.setUserEvaluationStatus(UserEvaluationStatusEnum.MARKING_COMPLETE.getCode());
                UserExamStatusEnum userExamStatusEnum = PassStatusEnum.PASS.getCode().equals(resp.getPassStatus()) ? UserExamStatusEnum.PASS : UserExamStatusEnum.FLUNK;
                resp.setUserExamStatus(userExamStatusEnum.getCode());
            }
        }
        return Result.success(respPage);
    }

    /**
     * 考试-监控数据-题目分析-分页
     *
     * @param req 分页请求参数
     * @return 题目分析
     */
    public Result<Page<AdminExamDataQuestionAnalysisPageResp>> questionAnalysisPage(AdminExamDataQuestionAnalysisPageReq req) {
        // 获取题目ID
        Set<Long> questionIds = new HashSet<>();
        Set<Long> userIds = new HashSet<>();
        if (ExamTypeEnum.MOCK.getCode().equals(req.getExamType()) || ExamTypeEnum.EXAMINATION.getCode().equals(req.getExamType())) {
            Exam exam = examDao.getById(req.getRelateId());
            if (ObjectUtil.isNull(exam)) {
                return Result.error(toI18n("resource.exam.not.exist"));
            }
            List<PaperQuestion> paperQuestionList = paperQuestionDao.listByPaperId(exam.getPaperId());
            if (CollectionUtil.isNotEmpty(paperQuestionList)) {
                paperQuestionList.forEach(item -> questionIds.add(item.getQuestionId()));
            }
        }

        // 计算正确率
        List<ExamUserAnswer> examUserAnswerList;
        examUserAnswerList = examUserAnswerDao.lambdaQuery()
                .eq(ExamUserAnswer::getExamType, req.getExamType())
                .eq(ExamUserAnswer::getRelateId, req.getRelateId())
                .in(CollectionUtil.isNotEmpty(userIds), ExamUserAnswer::getUserId, userIds)
                .list();

        Map<Long, BigDecimal> questionCorrectRateMap = new HashMap<>();
        if (CollectionUtil.isNotEmpty(examUserAnswerList)) {
            // 获取一级题目
            Map<Long, List<ExamUserAnswer>> firstMap = examUserAnswerList.stream()
                    .collect(Collectors.groupingBy(
                                    ExamUserAnswer::getQuestionId
                            )
                    );

            // 处理题目翻译
            firstMap.forEach((questionId, userAnswerList) -> {
                // 其他题型
                long correctCount = userAnswerList.stream().filter(item -> item.getUserScore().compareTo(BigDecimal.ZERO) > 0).count();
                BigDecimal correctRate = new BigDecimal(correctCount)
                        .multiply(BigDecimal.valueOf(100))
                        .divide(BigDecimal.valueOf(userAnswerList.size()), 2, RoundingMode.HALF_UP);
                questionCorrectRateMap.put(questionId, correctRate);
            });
        }

        if (CollectionUtil.isEmpty(questionIds)) {
            return Result.success(new Page<>());
        }

        LambdaQueryWrapper<Question> wrapper = new LambdaQueryWrapper<Question>()
                .in(Question::getId, questionIds)
                .eq(ObjectUtil.isNotNull(req.getQuestionType()), Question::getQuestionType, req.getQuestionType())
                .eq(ObjectUtil.isNotNull(req.getDifficultyLevel()), Question::getDifficultyLevel, req.getDifficultyLevel())
                .like(StrUtil.isNotBlank(req.getQuestionTitle()), Question::getQuestionTitle, req.getQuestionTitle())
                .orderByDesc(Question::getSort).orderByDesc(Question::getId);
        IPage<Question> page = questionDao.page(PageDTO.of(req.getPageCurrent(), req.getPageSize()), wrapper);
        Page<AdminExamDataQuestionAnalysisPageResp> respPage = PageUtil.transform(page, AdminExamDataQuestionAnalysisPageResp.class);
        if (ObjectUtil.isNull(respPage) || CollectionUtil.isEmpty(respPage.getList())) {
            return Result.success(respPage);
        }

        for (AdminExamDataQuestionAnalysisPageResp resp : respPage.getList()) {
            resp.setQuestionId(resp.getId());

            BigDecimal correctRate = questionCorrectRateMap.get(resp.getQuestionId());
            BigDecimal errorRate;
            if (ObjectUtil.isNotNull(correctRate)) {
                errorRate = new BigDecimal(100).subtract(correctRate);
            } else {
                correctRate = BigDecimal.ZERO;
                errorRate = BigDecimal.ZERO;
            }

            resp.setCorrectRate(correctRate);
            resp.setErrorRate(errorRate);
        }
        return Result.success(respPage);
    }

    /**
     * 考试-监控数据-题目分析-详情-分页
     *
     * @param req 请求参数
     * @return 监控数据-题目分析-详情
     */
    public Result<Page<AdminExamDataQuestionAnalysisDetailPageResp>> questionAnalysisDetailPage
    (AdminExamDataQuestionAnalysisDetailPageReq req) {
        LambdaQueryWrapper<ExamUserAnswer> wrapper = new LambdaQueryWrapper<ExamUserAnswer>()
                .eq(ExamUserAnswer::getExamType, req.getExamType())
                .eq(ExamUserAnswer::getRelateId, req.getRelateId())
                .eq(ExamUserAnswer::getQuestionId, req.getQuestionId())
                .gt(Boolean.TRUE.equals(req.getCorrect()), ExamUserAnswer::getUserScore, BigDecimal.ZERO)
                .le(!Boolean.TRUE.equals(req.getCorrect()), ExamUserAnswer::getUserScore, BigDecimal.ZERO)
                .orderByDesc(ExamUserAnswer::getId);
        IPage<ExamUserAnswer> page = examUserAnswerDao.page(PageDTO.of(req.getPageCurrent(), req.getPageSize()), wrapper);
        Page<AdminExamDataQuestionAnalysisDetailPageResp> respPage = PageUtil.transform(page, AdminExamDataQuestionAnalysisDetailPageResp.class);
        if (ObjectUtil.isNull(respPage) || CollectionUtil.isEmpty(respPage.getList())) {
            return Result.success(respPage);
        }

        Set<Long> userIds = respPage.getList().stream().map(AdminExamDataQuestionAnalysisDetailPageResp::getUserId).collect(Collectors.toSet());
        Map<Long, Users> userMap = new HashMap<>();
        List<Users> usersList = usersDao.listByIds(userIds);
        if (CollectionUtil.isNotEmpty(usersList)) {
            usersList.forEach(item -> userMap.put(item.getId(), item));
        }

        for (AdminExamDataQuestionAnalysisDetailPageResp resp : respPage.getList()) {
            resp.setExamTime(resp.getGmtCreate());

            Users user = userMap.get(resp.getUserId());
            if (ObjectUtil.isNotNull(user)) {
                resp.setMobile(user.getMobile());
                resp.setNickname(user.getNickname());
                resp.setUserHead(user.getUserHead());
            }
        }
        return Result.success(respPage);
    }

    /**
     * 考试-监控数据-排行榜-列表
     *
     * @param req 请求参数
     * @return 排行榜列表
     */
    public Result<Page<AdminExamDataRankPageResp>> rankPage(AdminExamDataRankPageReq req) {
        if (ExamTypeEnum.MOCK.getCode().equals(req.getExamType())) {
            // 模拟考试
            return examRankPage(req);
        } else if (ExamTypeEnum.EXAMINATION.getCode().equals(req.getExamType())) {
            // 正式考试
            return examRankPage(req);
        } else {
            // 其他类型
            return Result.error(toI18n("resource.exam.exam.type.not.supported.yet"));
        }
    }

    /**
     * 考试-考试排行榜分页
     *
     * @param req 排名参数
     * @return 排名结果
     */
    private Result<Page<AdminExamDataRankPageResp>> examRankPage(AdminExamDataRankPageReq req) {
        Set<Long> userIds = new HashSet<>();
        Map<Long, Users> userMap = new HashMap<>();
        if (StrUtil.isNotBlank(req.getMobile()) || StrUtil.isNotBlank(req.getNickname())) {
            List<Users> userList = usersDao.lambdaQuery()
                    .like(StrUtil.isNotBlank(req.getNickname()), Users::getNickname, req.getNickname())
                    .like(StrUtil.isNotBlank(req.getMobile()), Users::getMobile, req.getMobile())
                    .list();
            if (CollectionUtil.isEmpty(userList)) {
                return Result.success(new Page<>());
            }
            userList.forEach(item -> userMap.put(item.getId(), item));
        }

        Page<ExamUserRankPO> page = examUserDao.rankPage(req.getExamType(), req.getRelateId(), userIds, req.getPageCurrent(), req.getPageSize());
        if (ObjectUtil.isNull(page) || CollectionUtil.isEmpty(page.getList())) {
            return Result.success(new Page<>());
        }
        Page<AdminExamDataRankPageResp> respPage = PageUtil.transform(page, AdminExamDataRankPageResp.class);


        // 查询用户信息
        if (CollectionUtil.isEmpty(userMap)) {
            Set<Long> respUserIds = respPage.getList().stream().map(AdminExamDataRankPageResp::getUserId).collect(Collectors.toSet());
            List<Users> usersList = usersDao.listByIds(respUserIds);
            if (CollectionUtil.isNotEmpty(usersList)) {
                usersList.forEach(item -> userMap.put(item.getId(), item));
            }
        }

        // 处理响应信息
        for (AdminExamDataRankPageResp resp : respPage.getList()) {
            if (CollectionUtil.isNotEmpty(userMap)) {
                Users user = userMap.get(resp.getUserId());
                if (ObjectUtil.isNotNull(user)) {
                    resp.setNickname(user.getNickname());
                    resp.setUserHead(user.getUserHead());
                    resp.setMobile(user.getMobile());
                }
            }
        }
        return Result.success(respPage);
    }

    /**
     * 考试-监控数据-阅卷-列表
     *
     * @param req 请求参数
     * @return 阅卷列表
     */
    public Result<List<AdminExamDataEvaluationListResp>> evaluationList(AdminExamDataEvaluationListReq req) {
        LambdaQueryWrapper<ExamUserRecord> wrapper = new LambdaQueryWrapper<ExamUserRecord>()
                .eq(ExamUserRecord::getExamType, req.getExamType())
                .eq(ExamUserRecord::getRelateId, req.getRelateId())
                .eq(ObjectUtil.isNotNull(req.getUserId()), ExamUserRecord::getUserId, req.getUserId())
                // 考试中的记录不参与评阅
                .ne(ExamUserRecord::getExamStatus, ExamStatusEnum.NOT_OVER.getCode())
                .orderByDesc(ExamUserRecord::getSubmitTime)
                .orderByDesc(ExamUserRecord::getId);
        List<ExamUserRecord> recordList = examUserRecordDao.list(wrapper);
        if (CollectionUtil.isEmpty(recordList)) {
            return Result.success(Collections.emptyList());
        }

        Set<Long> userIds = recordList.stream().map(ExamUserRecord::getUserId).collect(Collectors.toSet());
        Map<Long, Users> userMap = new HashMap<>();
        List<Users> usersList = usersDao.listByIds(userIds);
        if (CollectionUtil.isNotEmpty(usersList)) {
            usersList.forEach(item -> userMap.put(item.getId(), item));
        }

        List<AdminExamDataEvaluationListResp> respList = new ArrayList<>();
        for (ExamUserRecord record : recordList) {
            AdminExamDataEvaluationListResp resp = new AdminExamDataEvaluationListResp()
                    .setUserId(record.getUserId())
                    .setRecordId(record.getId())
                    .setExamStatus(record.getExamStatus());
            Users user = userMap.get(record.getUserId());
            if (ObjectUtil.isNotNull(user)) {
                resp.setMobile(user.getMobile())
                        .setNickname(user.getNickname())
                        .setUserHead(user.getUserHead());
            }
            respList.add(resp);
        }
        return Result.success(respList);
    }

    /**
     * 考试-监控数据-阅卷-开始
     *
     * @param recordId 记录ID
     * @return 考试记录信息
     */
    public Result<AdminExamDataEvaluationStartResp> evaluationStart(Long recordId) {
        ExamUserRecord examUserRecord = examUserRecordDao.getById(recordId);
        if (ObjectUtil.isNull(examUserRecord)) {
            return Result.error(toI18n("resource.exam.user.record.not.exist"));
        }
        if (ExamStatusEnum.NOT_OVER.getCode().equals(examUserRecord.getExamStatus())) {
            return Result.error(toI18n("resource.exam.user.record.in.the.exam"));
        }

        // 获取试卷
        Paper paper = paperDao.getById(examUserRecord.getPaperId());
        if (ObjectUtil.isNull(paper)) {
            return Result.error(toI18n("resource.paper.not.exist"));
        }
        // 获取用户信息
        Users user = usersDao.getById(examUserRecord.getUserId());
        if (ObjectUtil.isNull(user)) {
            return Result.error(toI18n("user.users.not.exist"));
        }

        // 获取用户答案
        List<ExamUserAnswer> userAnswerList = examUserAnswerDao.listByRecordId(examUserRecord.getId());
        if (CollectionUtil.isEmpty(userAnswerList)) {
            return Result.error(toI18n("resource.exam.user.record.no.exam.questions.to.read"));
        }

        // 获取大题信息
        List<AdminExamDataEvaluationStartResp.ExamTitle> respTitles = getEvaluationStartTitles(paper.getId(), userAnswerList);

        // 处理阅卷请求参数
        AdminExamDataEvaluationStartResp resp = new AdminExamDataEvaluationStartResp()
                .setRecordId(examUserRecord.getId())
                .setExamType(examUserRecord.getExamType())
                .setExamName("")
                .setDescription("")
                .setAnswerTime(examUserRecord.getAnswerTime())
                .setPaperId(paper.getId())
                .setPaperName(paper.getPaperName())
                .setSubmitTime(examUserRecord.getSubmitTime())
                .setTotalScore(examUserRecord.getTotalScore())
                .setPassScore(paper.getPassScore())
                .setTitles(respTitles);
        if (ExamTypeEnum.MOCK.getCode().equals(examUserRecord.getExamType()) || ExamTypeEnum.EXAMINATION.getCode().equals(examUserRecord.getExamType())) {
            Exam exam = examDao.getById(examUserRecord.getRelateId());
            if (ObjectUtil.isNotNull(exam)) {
                resp.setExamName(exam.getExamName())
                        .setDescription(exam.getDescription());
            }
        }

        // 更新考试记录为批改中
        examUserRecordDao.lambdaUpdate()
                .eq(ExamUserRecord::getId, recordId)
                .set(ExamUserRecord::getExamStatus, ExamStatusEnum.CORRECTING.getCode())
                .update();

        // 正式考试同步修改状态
        if (examUserRecord.getExamType().equals(ExamTypeEnum.EXAMINATION.getCode())) {
            ExamUser examUser = examUserDao.getByExamTypeAndExamIdAndUserId(examUserRecord.getExamType(), examUserRecord.getRelateId(), examUserRecord.getUserId());
            if (ObjectUtil.isNotNull(examUser)) {
                examUserDao.lambdaUpdate()
                        .eq(ExamUser::getId, examUser.getId())
                        .set(ExamUser::getUserEvaluationStatus, UserEvaluationStatusEnum.REVIEWING_PAPER.getCode())
                        .update();
            }
        }
        return Result.success(resp);
    }

    private List<AdminExamDataEvaluationStartResp.ExamTitle> getEvaluationStartTitles(Long paperId, List<ExamUserAnswer> userAnswerList) {
        // 获取用户考试答案
        if (CollectionUtil.isEmpty(userAnswerList)) {
            return Collections.emptyList();
        }

        // 获取试卷大题
        Set<Long> titleIds = userAnswerList.stream().map(ExamUserAnswer::getTitleId).collect(Collectors.toSet());
        List<PaperTitle> titleList = paperTitleDao.sortListByIds(titleIds);
        if (CollectionUtil.isEmpty(titleList)) {
            return Collections.emptyList();
        }

        // 获取试卷题目
        List<PaperQuestion> paperQuestionList = paperQuestionDao.listByPaperId(paperId);
        Map<Long, Map<Long, PaperQuestion>> paperQuestionMap = new HashMap<>();
        if (CollectionUtil.isNotEmpty(paperQuestionList)) {
            Map<Long, Map<Long, PaperQuestion>> resultMap = paperQuestionList.stream()
                    .collect(Collectors.groupingBy(
                                    PaperQuestion::getTitleId,
                                    Collectors.toMap(PaperQuestion::getQuestionId, Function.identity())
                            )
                    );
            paperQuestionMap.putAll(resultMap);
        }

        // 获取试题
        Set<Long> questionIds = userAnswerList.stream().map(ExamUserAnswer::getQuestionId).collect(Collectors.toSet());
        List<QuestionDetailResp> questionList = questionCommonBiz.listDetailByIds(questionIds);
        Map<Long, QuestionDetailResp> questionMap = new HashMap<>();
        if (CollectionUtil.isNotEmpty(questionList)) {
            questionList.forEach(question -> questionMap.put(question.getId(), question));
        }

        Map<Long, List<ExamUserAnswer>> userAnswerMap = userAnswerList.stream().collect(Collectors.groupingBy(ExamUserAnswer::getTitleId));

        // 处理试题信息
        List<AdminExamDataEvaluationStartResp.ExamTitle> respTitles = BeanUtil.copyProperties(titleList, AdminExamDataEvaluationStartResp.ExamTitle.class);
        for (AdminExamDataEvaluationStartResp.ExamTitle respTitle : respTitles) {
            List<ExamUserAnswer> titleUserAnswerList = userAnswerMap.get(respTitle.getId());
            if (CollectionUtil.isEmpty(titleUserAnswerList)) {
                respTitle.setQuestions(Collections.emptyList());
                continue;
            }

            // 获取大题下的小题
            Map<Long, PaperQuestion> titleQuestionMap = paperQuestionMap.get(respTitle.getId());

            // 处理大题下试题信息
            List<AdminExamDataEvaluationStartResp.Question> respTitleQuestions = new ArrayList<>();
            for (ExamUserAnswer firstAnswer : titleUserAnswerList) {
                QuestionDetailResp question = questionMap.get(firstAnswer.getQuestionId());
                if (ObjectUtil.isNull(question)) {
                    continue;
                }

                // 返回题目对象
                AdminExamDataEvaluationStartResp.Question respTitleQuestion = new AdminExamDataEvaluationStartResp.Question()
                        .setQuestionId(firstAnswer.getQuestionId())
                        .setQuestionType(firstAnswer.getQuestionType())
                        .setQuestionScore(firstAnswer.getQuestionScore())
                        .setSort(firstAnswer.getSort())
                        .setUserAnswerId(firstAnswer.getId())
                        .setUserAnswer(firstAnswer.getUserAnswer())
                        .setUserScore(firstAnswer.getUserScore())
                        .setEvaluationComment(firstAnswer.getEvaluationComment())
                        // AI评阅相关
                        .setAiGrading(firstAnswer.getAiGrading())
                        .setAiUserScore(firstAnswer.getAiUserScore())
                        .setAiGradingReason(firstAnswer.getAiGradingReason())
                        // 处理题目信息
                        .setDifficultyLevel(question.getDifficultyLevel())
                        .setQuestionTitle(question.getQuestionTitle())
                        .setQuestionAnswer(question.getQuestionAnswer())
                        .setOptionCount(question.getOptionCount())
                        .setQuestionAnswerIds(question.getQuestionAnswerIds())
                        .setOptions(BeanUtil.copyProperties(question.getOptions(), AdminExamDataEvaluationStartResp.QuestionOption.class));

                // 处理试卷题目评分方式
                if (CollectionUtil.isNotEmpty(titleQuestionMap)) {
                    PaperQuestion paperQuestion = titleQuestionMap.get(firstAnswer.getQuestionId());
                    if (ObjectUtil.isNotNull(paperQuestion)) {
                        respTitleQuestion.setMultiScoreWay(paperQuestion.getMultiScoreWay())
                                .setMultiScore(paperQuestion.getMultiScore())
                                .setFillScoreWay(paperQuestion.getFillScoreWay())
                                .setFillScore(paperQuestion.getFillScore());
                    }
                }

                if (Boolean.FALSE.equals(firstAnswer.getEvaluationComplete())) {
                    respTitleQuestion.setUserAnswerStatus(respTitleQuestion.getUserScore().compareTo(BigDecimal.ZERO) > 0 ? UserAnswerStatusEnum.CORRECT.getCode() : UserAnswerStatusEnum.ERROR.getCode());
                } else {
                    respTitleQuestion.setUserAnswerStatus(UserAnswerStatusEnum.WAIT_CORRECTED.getCode());
                }
                respTitleQuestions.add(respTitleQuestion);
            }
            // 设置大题的题目
            respTitle.setQuestions(respTitleQuestions);
        }
        return respTitles;
    }

    /**
     * 考试-监控数据-阅卷-结束评阅
     *
     * @param req 评估结束参数
     * @return 处理结果
     */
    @Transactional(rollbackFor = Exception.class)
    public Result<String> evaluationEnd(AdminExamDataEvaluationEndReq req) {
        ExamUserRecord examUserRecord = examUserRecordDao.getById(req.getRecordId());
        if (ObjectUtil.isNull(examUserRecord)) {
            return Result.error(toI18n("resource.exam.user.record.not.exist"));
        }
        if (!ExamStatusEnum.CORRECTING.getCode().equals(examUserRecord.getExamStatus())) {
            return Result.error(toI18n("resource.exam.data.evaluation.end.status.error"));
        }

        // 获取考试记录答案
        List<ExamUserAnswer> userAnswerList = examUserAnswerDao.listByRecordId(req.getRecordId());
        Map<Long, ExamUserAnswer> userAnswerMap = new HashMap<>();
        if (CollectionUtil.isNotEmpty(userAnswerList)) {
            userAnswerList.forEach(item -> userAnswerMap.put(item.getId(), item));
        }

        List<ExamUserAnswer> updateUserAnswerList = new ArrayList<>();
        for (AdminExamDataEvaluationEndReq.EvaluationResult result : req.getResults()) {
            ExamUserAnswer userAnswer = userAnswerMap.get(result.getAnswerId());
            if (ObjectUtil.isNull(userAnswer)) {
                return Result.error(toI18n("resource.exam.data.answer.id.not.exist"));
            }
            if (userAnswer.getQuestionScore().compareTo(result.getUserScore()) < 0) {
                return Result.error(toI18n("resource.exam.data.user.score.gt.question.score"));
            }

            ExamUserAnswer updateUserAnswer = new ExamUserAnswer()
                    .setId(result.getAnswerId())
                    .setUserScore(result.getUserScore())
                    .setEvaluationComplete(Boolean.TRUE)
                    .setEvaluationComment(result.getEvaluationComment());
            updateUserAnswerList.add(updateUserAnswer);
        }
        // 批量修改用户答案
        if (CollectionUtil.isNotEmpty(updateUserAnswerList)) {
            examUserAnswerDao.updateBatchById(updateUserAnswerList);
        }

        // 统计用户得分
        List<ExamUserAnswer> examUserAnswerList = examUserAnswerDao.lambdaQuery()
                .eq(ExamUserAnswer::getRecordId, req.getRecordId())
                .eq(ExamUserAnswer::getUserId, examUserRecord.getUserId())
                .eq(ExamUserAnswer::getEvaluationComplete, Boolean.TRUE)
                .list();
        BigDecimal userScore = BigDecimal.ZERO;
        if (CollectionUtil.isNotEmpty(examUserAnswerList)) {
            userScore = examUserAnswerList.stream()
                    .map(ExamUserAnswer::getUserScore)
                    .reduce(BigDecimal.ZERO, BigDecimal::add);
        }

        // 更新考试记录
        Integer passStatus = examUserRecord.getPassScore().compareTo(userScore) <= 0 ? PassStatusEnum.PASS.getCode() : PassStatusEnum.FAIL.getCode();
        examUserRecordDao.lambdaUpdate()
                .eq(ExamUserRecord::getId, req.getRecordId())
                .set(ExamUserRecord::getUserScore, userScore)
                .set(ExamUserRecord::getExamStatus, ExamStatusEnum.COMPLETE.getCode())
                .set(ExamUserRecord::getPassStatus, passStatus)
                .update();


        if (ExamTypeEnum.MOCK.getCode().equals(examUserRecord.getExamType()) || ExamTypeEnum.EXAMINATION.getCode().equals(examUserRecord.getExamType())) {
            // 更新用户考试
            examCommonBiz.handleExamUser(examUserRecord);
        }
        return Result.success(toI18nSuccess());
    }

    /**
     * 考试-监控数据-阅卷-更新评阅状态
     *
     * @param req 更新评阅状态参数
     * @return 处理结果
     */
    public Result<String> evaluationUpdateStatus(AdminExamDataEvaluationUpdateStatusReq req) {
        // 获取考试记录
        ExamUserRecord examUserRecord = examUserRecordDao.getById(req.getRecordId());
        if (ObjectUtil.isNull(examUserRecord)) {
            return Result.error(toI18n("resource.exam.user.record.not.exist"));
        }

        // 更新评估状态
        examUserRecordDao.lambdaUpdate()
                .eq(ExamUserRecord::getId, req.getRecordId())
                .set(ExamUserRecord::getExamStatus, req.getExamStatus())
                .update();

        if (ExamTypeEnum.EXAMINATION.getCode().equals(examUserRecord.getExamType())) {
            // 正式考试同步修改状态
            ExamUser examUser = examUserDao.getByExamTypeAndExamIdAndUserId(examUserRecord.getExamType(), examUserRecord.getRelateId(), examUserRecord.getUserId());
            if (ObjectUtil.isNotNull(examUser)) {
                Integer userEvaluationStatus = req.getExamStatus().equals(ExamStatusEnum.WAIT_CORRECTED.getCode()) ? UserEvaluationStatusEnum.WAIT_MARKED.getCode() : UserEvaluationStatusEnum.MARKING_COMPLETE.getCode();
                examUserDao.lambdaUpdate()
                        .eq(ExamUser::getId, examUser.getId())
                        .set(ExamUser::getUserEvaluationStatus, userEvaluationStatus)
                        .update();
            }
        }
        return Result.success(toI18nSuccess());
    }

    /**
     * 考试-个人-分页
     *
     * @param req 查询参数
     * @return 个人考试
     */
    public Result<Page<AdminExamDataPersonalPageResp>> personalPage(AdminExamDataPersonalPageReq req) {
        Map<Long, Exam> examMap = new HashMap<>();
        if (StrUtil.isNotBlank(req.getExamName())) {
            List<Exam> examList = examDao.lambdaQuery()
                    .eq(ObjectUtil.isNotNull(req.getExamType()), Exam::getExamType, req.getExamType())
                    .like(Exam::getExamName, req.getExamName())
                    .list();
            if (CollectionUtil.isEmpty(examList)) {
                return Result.success(new Page<>());
            }
            examList.forEach(item -> examMap.put(item.getId(), item));
        }

        LocalDateTime endRegistrationTime = ObjectUtil.isNotNull(req.getEndRegistrationTime()) ? req.getEndRegistrationTime().plusMinutes(1) : null;
        LocalDateTime endExamStartTime = ObjectUtil.isNotNull(req.getEndExamStartTime()) ? req.getEndExamStartTime().plusMinutes(1) : null;
        LocalDateTime endExamSubmitTime = ObjectUtil.isNotNull(req.getEndExamSubmitTime()) ? req.getEndExamSubmitTime().plusMinutes(1) : null;

        // 查询考试记录
        LambdaQueryWrapper<ExamUser> wrapper = new LambdaQueryWrapper<ExamUser>()
                .eq(ExamUser::getUserId, req.getUserId())
                .in(CollectionUtil.isNotEmpty(examMap), ExamUser::getExamId, examMap.keySet())
                .ge(ObjectUtil.isNotNull(req.getBeginRegistrationTime()), ExamUser::getRegistrationTime, req.getBeginRegistrationTime())
                .lt(ObjectUtil.isNotNull(endRegistrationTime), ExamUser::getRegistrationTime, endRegistrationTime)
                .ge(ObjectUtil.isNotNull(req.getBeginExamStartTime()), ExamUser::getExamStartTime, req.getBeginExamStartTime())
                .lt(ObjectUtil.isNotNull(endExamStartTime), ExamUser::getExamStartTime, endExamStartTime)
                .ge(ObjectUtil.isNotNull(req.getBeginExamSubmitTime()), ExamUser::getExamSubmitTime, req.getBeginExamSubmitTime())
                .lt(ObjectUtil.isNotNull(endExamSubmitTime), ExamUser::getExamSubmitTime, endExamSubmitTime)
                .orderByDesc(ExamUser::getExamStartTime, ExamUser::getId);
        IPage<ExamUser> page = examUserDao.page(PageDTO.of(req.getPageCurrent(), req.getPageSize()), wrapper);
        Page<AdminExamDataPersonalPageResp> respPage = PageUtil.transform(page, AdminExamDataPersonalPageResp.class);
        if (ObjectUtil.isNull(respPage) || CollectionUtil.isEmpty(respPage.getList())) {
            return Result.success(respPage);
        }

        Set<Long> examIds = respPage.getList().stream().map(AdminExamDataPersonalPageResp::getExamId).collect(Collectors.toSet());
        if (CollectionUtil.isEmpty(examMap)) {
            List<Exam> examList = examDao.listByIds(examIds);
            if (CollectionUtil.isNotEmpty(examList)) {
                examList.forEach(item -> examMap.put(item.getId(), item));
            }
        }

        // 统计用户考试次数
        List<ExamUserRecordExamCountPO> examCountList = examUserRecordDao.examCountByUserIdAndRelateIds(req.getUserId(), examIds);
        Map<Long, Long> examCountMap = new HashMap<>();
        if (CollectionUtil.isNotEmpty(examCountList)) {
            examCountList.forEach(item -> examCountMap.put(item.getRelateId(), item.getCount()));
        }

        // 获取阅卷方式
        Set<Long> recordIds = respPage.getList().stream()
                .map(AdminExamDataPersonalPageResp::getExamRecordId)
                .filter(ObjectUtil::isNotNull)
                .collect(Collectors.toSet());
        List<ExamUserRecord> userRecordList = examUserRecordDao.listByIds(recordIds);
        Map<Long, ExamUserRecord> userRecordMap = new HashMap<>();
        if (CollectionUtil.isNotEmpty(userRecordList)) {
            userRecordList.forEach(item -> userRecordMap.put(item.getId(), item));
        }

        for (AdminExamDataPersonalPageResp resp : respPage.getList()) {
            Exam exam = examMap.get(resp.getExamId());
            if (ObjectUtil.isNotNull(exam)) {
                resp.setExamName(exam.getExamName());
            }

            ExamUserRecord examUserRecord = userRecordMap.get(resp.getExamRecordId());
            if (ObjectUtil.isNotNull(examUserRecord)) {
                resp.setEvaluationMethod(examUserRecord.getEvaluationMethod());
            }

            resp.setExamCount(examCountMap.getOrDefault(resp.getUserId(), 0L));
        }
        return Result.success(respPage);
    }
}
